'use strict';
const { existsSync, unlink } = require('fs-extra');
const deleteEmpty = require('delete-empty');
const log = require('./logger');
const nodePath = require('path');
const getTrackAlternative = require('./getTrackAlternative');
const downloadState = require('./downloadState');
const downloadTrack = require('../actions/downloadTrack');
const addTrackToPlaylist = require('./addTrackToPlaylist');
const addTrackTags = require('./addTrackTags');
const musicQualities = require('../musicQualities');
const deezerApi = require('deezer-request2');
const saveLayout = require('../actions/saveLayout');
const { getValidTrackQuality, getAlbumInfos, toTwoDigits } = require('./utils');
/**
 * Download a track + id3tags (album cover...) and save it in the downloads folder.
 *
 * @param {Number}  id
 * @param {Object}  trackInfos
 * @param {Object}  albumInfos
 * @param {Boolean} isAlternativeTrack
 * @param {Number}  numberRetry
 */
const downloadSingleTrack = async (
  id,
  dirPath,
  quality,
  trackInfos = {},
  albumInfos = {},
  isAlternativeTrack = false,
  numberRetry = 0
) => {
  numberRetry++;
  if (numberRetry >= 3) {
    return errorHandling({
      error: 'Error!',
      message:
        trackInfos.ALB_ART_NAME +
        ' - ' +
        trackInfos.SNG_TITLE_VERSION +
        '\n   › An error occurred, please try again later!',
    });
  }

  let saveFilePath,
    originalTrackInfos,
    trackQuality,
    fileExtension = 'mp3';

  log.debug('Start downloading "track/' + id + '"');
  if (Object.keys(trackInfos).length > 0) {
    originalTrackInfos = trackInfos;
    return await afterTrackInfoFetching();
  } else {
    try {
      const trackInfo = await deezerApi.getTrackInfo(id);
      originalTrackInfos = trackInfo.data.results;
      trackInfos = originalTrackInfos;
      return await afterTrackInfoFetching();
    } catch (err) {
      return errorHandling(err);
    }
  }

  async function afterTrackInfoFetching() {
    trackQuality = await getValidTrackQuality(originalTrackInfos, quality);

    if (!trackQuality) {
      return errorHandling(
        trackInfos.ALB_ART_NAME + ' - ' + trackInfos.SNG_TITLE + "\n  › Deezer doesn't provide the song anymore"
      );
    }

    originalTrackInfos.SNG_TITLE_VERSION = originalTrackInfos.SNG_TITLE;

    if (originalTrackInfos.VERSION) {
      originalTrackInfos.SNG_TITLE_VERSION = (originalTrackInfos.SNG_TITLE + ' ' + originalTrackInfos.VERSION).trim();
    }

    if (Object.keys(albumInfos).length == 0 && trackInfos.ALB_ID) {
      const downloadingMessage = trackInfos.ART_NAME + ' - ' + trackInfos.SNG_TITLE_VERSION;
      downloadState.update(originalTrackInfos.SNG_ID, downloadingMessage);

      const albumInfosResponse = await getAlbumInfos(trackInfos.ALB_ID);
      if (albumInfosResponse) {
        log.debug('Got album infos for album/' + trackInfos.ALB_ID);
        albumInfos = albumInfosResponse;
      }
    }
    return await afterAlbumInfoFetching();
  }

  async function afterAlbumInfoFetching() {
    if (albumInfos.PHYSICAL_RELEASE_DATE && !trackInfos.ALB_RELEASE_DATE) {
      originalTrackInfos.ALB_RELEASE_DATE = albumInfos.PHYSICAL_RELEASE_DATE;
    }

    originalTrackInfos.ALB_ART_NAME = originalTrackInfos.ART_NAME;

    if (albumInfos.ART_NAME) {
      originalTrackInfos.ALB_ART_NAME = albumInfos.ART_NAME;
    }

    if (!originalTrackInfos.ARTISTS || 0 === originalTrackInfos.ARTISTS.length) {
      originalTrackInfos.ARTISTS = [
        {
          ART_ID: originalTrackInfos.ART_ID,
          ART_NAME: originalTrackInfos.ALB_ART_NAME,
          ART_PICTURE: originalTrackInfos.ART_PICTURE,
        },
      ];
    }

    if ('various' === originalTrackInfos.ALB_ART_NAME.trim().toLowerCase()) {
      originalTrackInfos.ALB_ART_NAME = 'Various Artists';
    }

    const downloadingMessage = trackInfos.ALB_ART_NAME + ' - ' + trackInfos.SNG_TITLE_VERSION;
    downloadState.update(originalTrackInfos.SNG_ID, downloadingMessage);

    return await afterAlbumInfoOfficialApiFetching();
  }

  async function afterAlbumInfoOfficialApiFetching() {
    if (albumInfos.__TYPE__) {
      originalTrackInfos.ALB_RELEASE_TYPE = albumInfos.__TYPE__;
    }

    if (isAlternativeTrack) {
      trackInfos.DURATION = originalTrackInfos.DURATION;
      trackInfos.GAIN = originalTrackInfos.GAIN;
      trackInfos.LYRICS_ID = originalTrackInfos.LYRICS_ID;
      trackInfos.LYRICS = originalTrackInfos.LYRICS;
    } else {
      trackInfos = originalTrackInfos;
    }

    if (!trackInfos.ALB_ART_NAME.trim()) {
      trackInfos.ALB_ART_NAME = 'Unknown artist';
    }

    if (!trackInfos.ALB_TITLE.trim()) {
      trackInfos.ALB_TITLE = 'Unknown album';
    }

    if (albumInfos.ALB_NUM_DISCS > 1) {
      trackInfos.ALB_TITLE += ' (Disc ' + toTwoDigits(albumInfos.NUMBER_DISK) + ')';
    }

    if (trackInfos.TRACK_POSITION) {
      trackInfos.TRACK_POSITION = toTwoDigits(trackInfos.TRACK_POSITION);
    }

    saveFilePath = saveLayout(trackInfos, dirPath);

    if (musicQualities.qualities.FLAC.id === trackQuality.id) {
      fileExtension = 'flac';
    }

    saveFilePath += '.' + fileExtension;

    if (existsSync(saveFilePath) || downloadState.isCurrentlyDownloadingPathUsed(saveFilePath)) {
      addTrackToPlaylist(saveFilePath, trackInfos, false);
      const error = {
        message: trackInfos.ALB_ART_NAME + ' - ' + trackInfos.SNG_TITLE_VERSION + ' exists!\n› ' + saveFilePath,
        name: 'songAlreadyExists',
        path: saveFilePath,
      };
      return errorHandling(error);
    }

    downloadState.addCurrentlyDownloadingPath(saveFilePath);
    log.debug('Started downloading track/' + trackInfos.SNG_ID + ' in quality ' + trackQuality.id);

    try {
      if (!originalTrackInfos.MD5_ORIGIN) {
        if (originalTrackInfos.PUID) {
          originalTrackInfos.MD5_ORIGIN = originalTrackInfos.PUID;
        } else {
          let trackInfo = await deezerApi.getTrackInfo(originalTrackInfos.SNG_ID);
          if (trackInfo.data.results.PUID) {
            trackInfo.data.results.MD5_ORIGIN = trackInfo.data.results.PUID;
            originalTrackInfos = trackInfo.data.results;
          }
        }
      }
      const decryptedTrackBuffer = await downloadTrack(originalTrackInfos, trackQuality.id, saveFilePath);
      unlink(nodePath.dirname(saveFilePath) + nodePath.sep + originalTrackInfos.SNG_ID + trackQuality.id);
      return await onTrackDownloadComplete(decryptedTrackBuffer);
    } catch (error) {
      log.debug('Failed downloading track/' + trackInfos.SNG_ID + '. Error: ' + error);
      if (
        originalTrackInfos.FALLBACK &&
        originalTrackInfos.FALLBACK.SNG_ID &&
        trackInfos.SNG_ID !== originalTrackInfos.FALLBACK.SNG_ID &&
        originalTrackInfos.SNG_ID !== originalTrackInfos.FALLBACK.SNG_ID
      ) {
        downloadState.removeCurrentlyDownloadingPath(saveFilePath);
        downloadState.remove(originalTrackInfos.SNG_ID);
        log.debug('Using alternative track/' + originalTrackInfos.FALLBACK.SNG_ID + ' for track/' + trackInfos.SNG_ID);
        deleteEmpty.sync(nodePath.dirname(saveFilePath));
        return await downloadSingleTrack(
          originalTrackInfos.FALLBACK.SNG_ID,
          dirPath,
          quality,
          originalTrackInfos.FALLBACK,
          {},
          true,
          numberRetry
        );
      } else {
        try {
          deleteEmpty.sync(nodePath.dirname(saveFilePath));
          const alternativeTrackInfos = await getTrackAlternative(trackInfos);
          downloadState.removeCurrentlyDownloadingPath(saveFilePath);
          downloadState.remove(originalTrackInfos.SNG_ID);

          log.debug('Using alternative track/' + alternativeTrackInfos.SNG_ID + ' for track/' + trackInfos.SNG_ID);

          return await downloadSingleTrack(
            alternativeTrackInfos.SNG_ID,
            dirPath,
            quality,
            alternativeTrackInfos,
            {},
            true,
            numberRetry
          );
        } catch (err) {
          const errorMessage =
            trackInfos.ALB_ART_NAME +
            ' - ' +
            trackInfos.SNG_TITLE_VERSION +
            "\n  › Deezer doesn't provide the song anymore";

          return errorHandling(errorMessage);
        }
      }
    }
  }

  async function onTrackDownloadComplete(decryptedTrackBuffer) {
    let successMessage = trackInfos.ALB_ART_NAME + ' - ' + trackInfos.SNG_TITLE_VERSION;

    if (isAlternativeTrack) {
      successMessage +=
        '\n  › Used ' +
        originalTrackInfos.ALB_ART_NAME +
        ' - ' +
        originalTrackInfos.SNG_TITLE_VERSION +
        ' #' +
        originalTrackInfos.SNG_ID +
        ' as alternative';
    }

    if (trackQuality.id !== quality) {
      let selectedMusicQualityName =
        musicQualities.qualities[
          Object.keys(musicQualities.qualities).find((key) => musicQualities.qualities[key].id === quality)
        ].name;
      successMessage +=
        '\n  › Used ' + trackQuality.name + ' because ' + selectedMusicQualityName + " wasn't available";
    }

    successMessage += '\n  › ' + saveFilePath;

    try {
      downloadState.success(originalTrackInfos.SNG_ID, successMessage);
      downloadState.removeCurrentlyDownloadingPath(saveFilePath);

      addTrackToPlaylist(saveFilePath, trackInfos);
      return await addTrackTags(decryptedTrackBuffer, trackInfos, albumInfos, saveFilePath);
    } catch (err) {
      successMessage += '\n  › Failed writing ID3 tags';
      downloadState.warn(originalTrackInfos.SNG_ID, successMessage);
      downloadState.removeCurrentlyDownloadingPath(saveFilePath);

      addTrackToPlaylist(saveFilePath, trackInfos);
      return { path: saveFilePath };
    }
  }

  function errorHandling(err) {
    log.debug(err);
    if (saveFilePath) {
      deleteEmpty.sync(nodePath.dirname(saveFilePath));
    }

    if (404 === err.statusCode) {
      err = 'Track #' + id + ' not found';
    }

    if (err.name && err.message) {
      if ('-' !== err.message) {
        if ('songAlreadyExists' === err.name) {
          //downloadState.success(originalTrackInfos.SNG_ID, err.message);
          downloadState.messages.push({
            type: 'note',
            message: err.message,
          });
        } else {
          // downloadState.fail(originalTrackInfos.SNG_ID, err.message);
          downloadState.messages.push({
            type: 'fatal',
            message: err.message,
          });
        }
      }
    } else {
      downloadState.fail(id, err);
    }

    if ('notAvailableButAlternative' !== err.name && 'invalidApiToken' !== err.name) {
      return err;
    }
  }
};

module.exports = downloadSingleTrack;
